using System.Diagnostics;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    public partial class Sqlite3
    {
        /*
    ** 2009 November 25
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    **
    ** This file contains code used to insert the values of host parameters
    ** (aka "wildcards") into the SQL text output by sqlite3_trace().
    */
        //#include "sqliteInt.h"
        //#include "vdbeInt.h"

#if !SQLITE_OMIT_TRACE

        /*
** zSql is a zero-terminated string of UTF-8 SQL text.  Return the number of
** bytes in this text up to but excluding the first character in
** a host parameter.  If the text contains no host parameters, return
** the total number of bytes in the text.
*/

        private static int findNextHostParameter(string zSql, int iOffset, ref int pnToken)
        {
            int tokenType = 0;
            int nTotal = 0;
            int n;

            pnToken = 0;
            while (iOffset < zSql.Length)
            {
                n = Tokenize.GetToken(zSql, iOffset, ref tokenType);
                Debug.Assert(n > 0 && tokenType != TokenKeyword.TK_ILLEGAL);
                if (tokenType == TokenKeyword.TK_VARIABLE)
                {
                    pnToken = n;
                    break;
                }
                nTotal += n;
                iOffset += n; // zSql += n;
            }
            return nTotal;
        }

        /*
    ** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which
    ** holds a copy of zRawSql but with host parameters expanded to their
    ** current bindings.
    **
    ** The calling function is responsible for making sure the memory returned
    ** is eventually freed.
    **
    ** ALGORITHM:  Scan the input string looking for host parameters in any of
    ** these forms:  ?, ?N, $A, @A, :A.  Take care to avoid text within
    ** string literals, quoted identifier names, and comments.  For text forms,
    ** the host parameter index is found by scanning the perpared
    ** statement for the corresponding OPCode.OP_Variable opcode.  Once the host
    ** parameter index is known, locate the value in p->aVar[].  Then render
    ** the value as a literal in place of the host parameter name.
    */

        private static string sqlite3VdbeExpandSql(
            Vdbe p, /* The prepared statement being evaluated */
            string zRawSql /* Raw text of the SQL statement */
            )
        {
            sqlite3 db; /* The database connection */
            int idx = 0; /* Index of a host parameter */
            int nextIndex = 1; /* Index of next ? host parameter */
            int n; /* Length of a token prefix */
            int nToken = 0; /* Length of the parameter token */
            int i; /* Loop counter */
            Mem pVar; /* Value of a host parameter */
            var _out = new StrAccum(1000); /* Accumulate the _output here */
            //StringBuilder zBase = new StringBuilder( 100 ); /* Initial working space */
            int izRawSql = 0;

            db = p.db;
            Print.StrAccumInit(_out, null, 100,
                                db.aLimit[LimitCategory.SQLITE_LIMIT_LENGTH]);
            _out.db = db;
            while (izRawSql < zRawSql.Length)
            {
                n = findNextHostParameter(zRawSql, izRawSql, ref nToken);
                Debug.Assert(n > 0);
                Print.StrAccumAppend(_out, zRawSql.Substring(izRawSql, n), n);
                izRawSql += n;
                Debug.Assert(izRawSql < zRawSql.Length || nToken == 0);
                if (nToken == 0) break;
                if (zRawSql[izRawSql] == '?')
                {
                    if (nToken > 1)
                    {
                        Debug.Assert(SqliteInt.Sqlite3Isdigit(zRawSql[izRawSql + 1]));
                        Utility.Sqlite3GetInt32(zRawSql, izRawSql + 1, ref idx);
                    }
                    else
                    {
                        idx = nextIndex;
                    }
                }
                else
                {
                    Debug.Assert(zRawSql[izRawSql] == ':' || zRawSql[izRawSql] == '$' || zRawSql[izRawSql] == '@');
                    UnitTest.TestCase(zRawSql[izRawSql] == ':');
                    UnitTest.TestCase(zRawSql[izRawSql] == '$');
                    UnitTest.TestCase(zRawSql[izRawSql] == '@');
                    idx = sqlite3VdbeParameterIndex(p, zRawSql.Substring(izRawSql, nToken), nToken);
                    Debug.Assert(idx > 0);
                }
                izRawSql += nToken;
                nextIndex = idx + 1;
                Debug.Assert(idx > 0 && idx <= p.nVar);
                pVar = p.aVar[idx - 1];
                if ((pVar.flags & MEMFlag.MEM_Null) != 0)
                {
                    Print.StrAccumAppend(_out, "NULL", 4);
                }
                else if ((pVar.flags & MEMFlag.MEM_Int) != 0)
                {
                    Print.XPrintf(_out, "%lld", pVar.u.i);
                }
                else if ((pVar.flags & MEMFlag.MEM_Real) != 0)
                {
                    Print.XPrintf(_out, "%!.15g", pVar.r);
                }
                else if ((pVar.flags & MEMFlag.MEM_Str) != 0)
                {
#if !SQLITE_OMIT_UTF16
      byte enc = Helper.ENC(db);
      if( enc!=Const.SQLITE_UTF8 ){
        Mem utf8;
        memset(&utf8, 0, sizeof(utf8));
        utf8.db = db;
        sqlite3VdbeMemSetStr(&utf8, pVar.z, pVar.n, enc, Const.SQLITE_STATIC);
        sqlite3VdbeChangeEncoding(&utf8, Const.SQLITE_UTF8);
        Print.XPrintf(_out, "'%.*q'", utf8.n, utf8.z);
        sqlite3VdbeMemRelease(&utf8);
      }else
#endif
                    {
                        Print.XPrintf(_out, "'%.*q'", pVar.n, pVar.z);
                    }
                }
                else if ((pVar.flags & MEMTermFlag.MEM_Zero) != 0)
                {
                    Print.XPrintf(_out, "zeroblob(%d)", pVar.u.nZero);
                }
                else
                {
                    Debug.Assert((pVar.flags & MEMFlag.MEM_Blob) != 0);
                    Print.StrAccumAppend(_out, "x'", 2);
                    for (i = 0; i < pVar.n; i++)
                    {
                        Print.XPrintf(_out, "%02x", pVar.zBLOB[i] & 0xff);
                    }
                    Print.StrAccumAppend(_out, "'", 1);
                }
            }
            return Print.StrAccumFinish(_out);
        }

#endif
        //* #if !SQLITE_OMIT_TRACE */
    }
}